Java JavaScript Python C# C C++ Go Kotlin PHP Swift R Ruby TypeScript Scala SQL Perl rust VisualBasic Matlab Julia

Generics → Type Parameters and arguments

Generics

Type Parameters and arguments

Type Parameters and Arguments in Java: A Detailed Explanation

Type parameters and arguments are fundamental concepts in Java's generics system, introduced in Java 5. They allow you to write code that can work with different data types without sacrificing type safety. Instead of writing separate code for each type, you write it once using placeholders (type parameters) which are then filled in with actual types (type arguments) when the code is used.

1. Type Parameters

Type parameters are placeholders for types that are specified later. They are declared within angle brackets `<>` after the class name, method name, or interface name. They are typically single uppercase letters like `T`, `K`, `V`, `E` (for common uses like Type, Key, Value, Element), but you can use more descriptive names if needed.
Type Parameters basic example public class Box<T> { // T is a type parameter private T content; public Box(T content) { this.content = content; } public T getContent() { return content; } }
In this example, `T` is a type parameter. `Box<T>` defines a `Box` class that can hold any type of object, represented by `T`. The `content` variable and the methods are all parameterized by `T`.

2. Type Arguments

Type arguments are the actual types that replace the type parameters when you create an instance of a generic class or call a generic method. They are provided within angle brackets `<>` when you instantiate or invoke the generic element.
Example (using the `Box` class) public class Main { public static void main(String[] args) { Box<Integer> integerBox = new Box<>(10); // Integer is the type argument Integer value1 = integerBox.getContent(); // value1 is an Integer Box<String> stringBox = new Box<>("Hello"); // String is the type argument String value2 = stringBox.getContent(); // value2 is a String //Box<int> intBox = new Box<>(10); //Error: primitive types not allowed as type arguments. Box<Number> numberBox = new Box<>(10.5); //Number is a type argument, accepts Integer and Double. Number value3 = numberBox.getContent(); } }
Here, `Integer` and `String` are type arguments. They specify that `integerBox` will hold `Integer` objects, and `stringBox` will hold `String` objects. The compiler uses these arguments to ensure type safety; you can't accidentally put a `String` into `integerBox`. Note that primitive types (like `int`, `double`) cannot be used directly as type arguments; you must use their wrapper classes (`Integer`, `Double`).

3. Multiple Type Parameters

You can define classes and methods with multiple type parameters.
public class Pair<K, V> { // K and V are type parameters for Key and Value private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } public class Main { public static void main(String[] args){ Pair<String, Integer> pair1 = new Pair<>("Apple", 1); Pair<Integer, Double> pair2 = new Pair<>(1, 3.14); } }

Output

Pair 1: Key: Apple, Value: 1 Pair 2: Key: 1, Value: 3.14
`Pair<K, V>` uses two type parameters, `K` for the key and `V` for the value. In the `main` method, we create instances with different type combinations: `String` and `Integer` for `pair1`, and `Integer` and `Double` for `pair2`.

4. Type Inference

Java's compiler can often infer the type arguments you intend to use, so you don't always need to explicitly specify them. This is called type inference.
Type inference basic syntax Box<Integer> integerBox = new Box<>(10); // Type argument Integer is explicitly specified Box<Integer> inferredBox = new Box<>(20); // Type argument Integer is inferred by the compiler.
In the second line, the compiler automatically infers that the type argument is `Integer` because the argument passed to the constructor is an `Integer`.

5. Upper Bounded Wildcards

Wildcards (`?`) allow you to write more flexible generic code. Upper bounded wildcards restrict the type to be a subtype of a specified type.
Upper Bounded Wildcards basic syntax public static void printBoxContent(Box<? extends Number> box) { // ? extends Number System.out.println(box.getContent()); }
`? extends Number` means the type argument can be `Number` or any subtype of `Number` (e.g., `Integer`, `Double`, `Float`). This allows the `printBoxContent` method to work with boxes containing various numeric types.

6. Lower Bounded Wildcards

Lower bounded wildcards specify that the type argument must be a supertype of a given type.
basic syntax public static void addNumberToBox(Box<? super Integer> box, Integer num) { box.setContent(num); }
`? super Integer` means the type argument can be `Integer` or any supertype of `Integer` (e.g., `Number`, `Object`). This allows the `addNumberToBox` method to add an `Integer` to a box that holds `Number` or `Object`.

7. Unbounded Wildcards

An unbounded wildcard (`<?>`) places no restrictions on the type argument.
public static void printAnything(Box<?> box){ System.out.println(box.getContent()); }
`<?>` means the type argument can be *any* type. However, you can't call methods on the `getContent()` unless you know the specific type within the box.
Understanding type parameters and arguments is crucial for writing efficient and type-safe Java code using generics. These concepts enhance code reusability and maintainability by allowing you to write algorithms that operate on a variety of data types without compromising type safety. Remember to use appropriate wildcards when dealing with collections of different types to add flexibility to your generic code.

Tutorials